package com.blog.service.Impl;
import com.alibaba.fastjson.JSON;
import com.blog.SystemMsg.SystemMsgProducer;
import com.blog.Utils.ConsetUtils;
import com.blog.Utils.RedisKeyUtil;
import com.blog.Utils.SensitiveWordUtils;
import com.blog.bean.domain.Comment;
import com.blog.bean.domain.DiscussPost;
import com.blog.bean.domain.Message;
import com.blog.bean.domain.User;
import com.blog.bean.dto.PostDTO;
import com.blog.bean.vo.*;
import com.blog.mapper.DiscussPostMapper;
import com.blog.mapper.MessageMapper;
import com.blog.mapper.UserMapper;
import com.blog.service.CommentService;
import com.blog.service.DiscussPostService;
import com.blog.service.LikeService;
import com.blog.service.MessageService;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.util.HtmlUtils;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 * @author 任浩
 * @version 1.0
 */

@Service
@Slf4j
public class DiscussPostServiceImpl implements DiscussPostService {

    @Autowired
    private DiscussPostMapper discussPostMapper;
    @Autowired
    private SensitiveWordUtils sensitiveWordUtils;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private CommentService commentService;
    @Autowired
    private LikeService likeService;
    @Autowired
    private SystemMsgProducer systemMsgProducer;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private MessageService messageService;
    @Autowired
    private MessageMapper messageMapper;
    @Autowired
    private RedisUtils redisUtils;

//    @Value("caffeine.post.maxsize")
//    private int size;
//    @Value("caffeine.post.expire_seconds")
//    private int seconds;

//    //得到本地缓存的帖子列表
//    private LoadingCache<String , List<PageVO>> postList;
//    //存放当前的总的数据。
//    private LoadingCache<String, Long> postTotal;

    /**
     * 初始化帖子列表的缓存
     *
     * 有一个问题，我这里了容器里的对象，但是@PostConstruct是当前方法比容器的类创建的早，所以导致了当前方法的出错，
     * 只能到数据库老老实实的为本地缓存初始化。
     */
//    @PostConstruct
//    public void init(){
//        //初始化本地缓存，帖子的列表，这个帖子列表我们只缓存首页的访问热度的帖子。
//        postList = Caffeine.newBuilder()
//                .maximumSize(size)
//                .expireAfterWrite(seconds, TimeUnit.SECONDS)
//                .build(new CacheLoader<String, List<PageVO>>() {
//                    @Override
//                    public @Nullable List<PageVO> load(@NonNull String key) throws Exception {
//                        //这里解决缓存数据的来源。
//                        if(key == null || key.length() == 0){
//                            throw new IllegalAccessException("传入的key参数错误");
//                        }
//
//                        String[] split = key.split(":");
//                        if (split == null || split.length != 2){
//                            throw  new IllegalAccessException("传入的key参数错误");
//                        }
//
//                        //在这里得到数据可以先到 二级缓存分布式缓存：redis里面查询有没有数据，没有数据再到数据库查询数据
//                        //到数据库初始化得到数据。
//                        Integer page = Integer.parseInt(split[0]);
//                        Integer pageNum = Integer.parseInt(split[1]);
//
//                        PageHelper.startPage(page,pageNum);
//                        Page<DiscussPost> page1 = discussPostMapper.queryPost("",1);
//                        List<DiscussPost> result = page1.getResult();
//
//                        log.info("到数据库初始化加载缓存数据。");
//
//                        List<PageVO> list = new ArrayList<>();
//
//                        //根据查询出来帖子的userid,去查询用户表，得到帖子的头像等信息。
//                        for (DiscussPost discussPost : result){
//                            PageVO pageVO = discussPostMapper.selectById(Integer.parseInt(discussPost.getUserId()));
//                            BeanUtils.copyProperties(discussPost,pageVO);
//
//                            //根据查询出来的帖子的id，查询出帖子的点赞信息。
//                            LikeVO likeVO = likeService.queryLike(pageVO.getId(),null);
//                            pageVO.setLikeVO(likeVO);
//
//                            list.add(pageVO);
//                        }
//                        return list;
//                    }
//                });
//        //初始化热度帖子总数据的本地缓存。
//        postTotal = Caffeine.newBuilder()
//                .maximumSize(size)
//                .expireAfterWrite(seconds,TimeUnit.SECONDS)
//                .build(new CacheLoader<String, Long>() {
//                    @Override
//                    public @Nullable Long load(@NonNull String key) throws Exception {
//                        String[] split = key.split(":");
//                        if (split == null || split.length != 2){
//                            throw  new IllegalAccessException("传入的key参数错误");
//                        }
//                        //在这里得到数据可以先到 二级缓存分布式缓存：redis里面查询有没有数据，没有数据再到数据库查询数据
//                        //到数据库初始化得到数据。
//                        Integer page = Integer.parseInt(split[0]);
//                        Integer pageNum = Integer.parseInt(split[1]);
//
//                        PageHelper.startPage(page,pageNum);
//                        Page<DiscussPost> page1 = discussPostMapper.queryPost("",1);
//                        return page1.getTotal();
//                    }
//                });
//    }


    @Override
    public PageAndUserVO queryPost(int page, int pageNum, Integer userId, Integer scoreMode, Integer unreadUserId) {

        //当前没有登录，并且访问热度排行的时候，就去访问本地缓存，看看有没有数据.
//        if((userId == null || userId ==0) && scoreMode == 1){
//            PageAndUserVO pageAndUserVO = new PageAndUserVO();
//            pageAndUserVO.setCurPage(page);
//            List<PageVO> list = postList.get(page + ":" + pageNum);
//            pageAndUserVO.setData(list);
//            pageAndUserVO.setScoreMode(scoreMode);
//            pageAndUserVO.setTotal(postTotal.get(page + ":" + pageNum));
//
//        }
//        log.info("到数据库取出数据");

        //开启分页插件
        PageHelper.startPage(page,pageNum);
        Page<DiscussPost> page1 = new Page<>();
        if(userId == null){
            page1 = discussPostMapper.queryPost("",scoreMode);
        }
        else {
            page1 = discussPostMapper.queryPost(userId.toString(),scoreMode);
        }

        List<DiscussPost> result = page1.getResult();

        List<PageVO> list = new ArrayList<>();
        PageAndUserVO pageAndUserVO = new PageAndUserVO();
        pageAndUserVO.setTotal(page1.getTotal());

        //根据查询出来帖子的userid,去查询用户表，得到帖子的头像等信息。
        for (DiscussPost discussPost : result){
            PageVO pageVO = discussPostMapper.selectById(Integer.parseInt(discussPost.getUserId()));
            BeanUtils.copyProperties(discussPost,pageVO);

            //根据查询出来的帖子的id，查询出帖子的点赞信息。
            LikeVO likeVO = likeService.queryLike(pageVO.getId(),unreadUserId);
            pageVO.setLikeVO(likeVO);

            list.add(pageVO);
        }

        pageAndUserVO.setData(list);
        pageAndUserVO.setCurPage(page);
        pageAndUserVO.setScoreMode(scoreMode);

        //计算当前总共的私信消息和系统消息的数量。
        Integer unreadTotal = -1;
        if(unreadUserId != null){
            unreadTotal = 0;
            Message message = new Message();
            message.setFromId(1);
            message.setToId(unreadUserId);
            message.setConversationId("like");
            message.setStatus(0);
            Integer count = messageMapper.topicUnreadCount(message);
            unreadTotal += count;
            message.setConversationId("comment");
            count = messageMapper.topicUnreadCount(message);

            unreadTotal += count;
            message.setConversationId("attention");
            count = messageMapper.topicUnreadCount(message);
            unreadTotal += count;

            count = messageMapper.unreadMsg(null, unreadUserId);
            unreadTotal += count;  //设置当前用户总的未读的消息数
        }

        pageAndUserVO.setUnreadTotal(unreadTotal);

        return pageAndUserVO;
    }

    /**
     * 添加帖子
     * @param discussPost
     */
    @Override
    public void add(DiscussPost discussPost,Integer userid) {
        //设置用户id
        discussPost.setUserId(userid.toString());
        //设置帖子的类型
        discussPost.setType(0);
        //设置帖子的状态
        discussPost.setStatus(0);
        //设置当前的发帖时间
        discussPost.setCreateTime(LocalDateTime.now());
        //设置评论数
        discussPost.setCommentCount(0);

        //将标题和内容进行过滤一下。
        //有可能标题和内容使用的是标签，那么直接在页面上显示的话会出问题，那么我们要对html标签进行转义。把它标记为文本内容。
        discussPost.setTitle(HtmlUtils.htmlEscape(discussPost.getTitle()));
        discussPost.setContent(HtmlUtils.htmlEscape(discussPost.getContent()));

        //对内容的敏感词进行过滤。
        StringBuffer filterContext = sensitiveWordUtils.filter(discussPost.getContent());
        discussPost.setContent(filterContext.toString());

        //将当前帖子的数据保存到数据库中。
        discussPostMapper.add(discussPost);

        //将当前的帖子信息，发送到消息队列，使消息队列消费将新增的帖子的数据保存到es中。
        String msg = JSON.toJSONString(discussPost);
        systemMsgProducer.searchProducer(msg);
    }

    /**
     * 根据用户id查找帖子的详情。
     * @param postDTO
     * @return
     */
    @Override
    public PostAndCommentVO queryPostByUserId(PostDTO postDTO, Integer userId) {
        log.info("查看帖子详情的方法：传递的数据为");
        System.out.println(postDTO);

        PostAndCommentVO postAndCommentVO = new PostAndCommentVO();

        User user = new User();
        user.setId(Integer.parseInt(postDTO.getUserId()));
        user = userMapper.check(user);
        PageVO pageVO = discussPostMapper.queryPostDetail(postDTO.getUserId(),postDTO.getTitle());
        //将从数据库查询出来的浏览量和redis查出来的浏览量合到一起。
        String key =RedisKeyUtil.getViewCountKey(postDTO.getId());
        //先判断当前key是否存在，不存在代表没有浏览量
        boolean exist = redisUtils.isExist(key);

        if(exist){
            Integer viewCount = redisUtils.getViewCount(RedisKeyUtil.getViewCountKey(postDTO.getId()));
            log.info("从缓存查询出来的浏览量为：" + viewCount);
            pageVO.setViewCount((viewCount == null ? 0 : viewCount) + (pageVO.getViewCount() == null ? 0 : pageVO.getViewCount()));
            log.info("合并浏览量成功");
        }

        int postid = pageVO.getId();
        int type = pageVO.getType();
        int status = pageVO.getStatus();
        BeanUtils.copyProperties(user,pageVO);
        pageVO.setId(postid);
        pageVO.setType(type);
        pageVO.setStatus(status);
        log.info("帖子的信息为:" + pageVO);

        //将user的数据拷贝到pageVO中，返回给客户端
        /**
         * 查询当前帖子的点赞的信息。
         */

        LikeVO likeVO = likeService.queryLike(Integer.parseInt(postDTO.getId()), null);
        pageVO.setLikeVO(likeVO);

        postAndCommentVO.setPageVO(pageVO);

        //进行查询楼主评论。
        Comment comment = new Comment();
        comment.setEntityType(ConsetUtils.ENTITY_TYPE);
        comment.setEntityId(pageVO.getId());
        comment.setTargetId(0);

        PostAndCommentVO temPostAndComment = commentService.
                                                queryComment(comment,postDTO.getPage(),postDTO.getPageNum(),userId);

        postAndCommentVO.setTotal(temPostAndComment.getTotal());
        postAndCommentVO.setCurPage(postDTO.getPage());
        postAndCommentVO.setCommentListVO(temPostAndComment.getCommentListVO());
        postAndCommentVO.setCurCode(0);
        /**
         * 判断当前帖子的id和当前用户的id是否一样。如果一样的话，那么就代表当前帖子是当前用户发表的。
         */

        log.info("帖子的用户id为：" + postDTO.getUserId() );
        log.info("用户id:" + userId);
        if((int) Integer.parseInt(postDTO.getUserId()) == (int) userId){
            postAndCommentVO.setCurCode(1);
        }

        return postAndCommentVO;
    }

    /**
     * 通过帖子id，查找帖子的信息。
     * @param discussPost
     * @return
     */
    @Override
    public DiscussPost queryPostByPostId(DiscussPost discussPost) {
        DiscussPost temdiscussPost = discussPostMapper.queryPostByPostId(discussPost);
        return temdiscussPost;
    }

    /**
     * 对帖子进行置顶的操作。
     * @param postId
     */
    @Override
    public void setTop(Integer postId) {
        discussPostMapper.setTop(postId);
        //对进行置顶的帖子的操作。要把保存早es中的数据进行修改。
        //然后通过帖子id查找到当前帖子的所有信息，对保存在es中的帖子数据进行修改。
        DiscussPost discussPost = new DiscussPost();
        discussPost.setId(postId);
        discussPost = discussPostMapper.queryPostByPostId(discussPost);
        String msg = JSON.toJSONString(discussPost);
        //把数据加入到消息队列里面的作用是进行热度更新。
        systemMsgProducer.producer(msg);
    }

    /**
     * 对帖子进行加精操作。
     * @param postId
     */
    @Override
    public void setWonderful(Integer postId) {
        discussPostMapper.setWonderful(postId);
        //然后通过帖子id查找到当前帖子的所有信息，对保存在es中的帖子数据进行修改。
        DiscussPost discussPost = new DiscussPost();
        discussPost.setId(postId);
        discussPost = discussPostMapper.queryPostByPostId(discussPost);
        String msg = JSON.toJSONString(discussPost);

        //对当前进行加精的帖子进行热度分数更新，把当前的帖子id保存到redis中，然后使用定时线程池，处理业务,完成帖子的热度更新操作。
        //使用redis的set数据结构保存，所有要更新热度的帖子id。
        //点赞，评论，加精都会让帖子的热度更新。
        //得到保存到redis中的key
        String scoreKey = RedisKeyUtil.scoreInit();
        redisTemplate.opsForSet().add(scoreKey,postId);

        systemMsgProducer.producer(msg);
    }


    /**
     * 对帖子的删除操作
     * @param postId
     */
    @Override
    public void delete(Integer postId) {
        discussPostMapper.delete(postId);
        //对保存在es中的帖子数据进行删除。
        systemMsgProducer.searchDeleteProducer(postId.toString());
        //还要把帖子中的评论给删除。
        commentService.delete(postId);
        log.info("删除帖子成功。");
    }

    /**
     * 将分数保存到数据库中。
     * @param discussPost
     */
    @Override
    public void updateScore(DiscussPost discussPost) {
        discussPostMapper.updateScore(discussPost);
    }
}
